#include <modules\graphics\animated_entity.h>

namespace DelGE
{
	namespace Modules
	{
		namespace Graphics
		{
			AnimatedEntity::AnimatedEntity()
			{
				this->vPoses = vector<EntityPose*>();

				this->vRenderModels = vector<Model*>();

				this->SetScale(1, 1, 1);
				this->SetRotation(0, 0, 0);
				this->SetPosition(0, 0, 0);
			}

			AnimatedEntity::AnimatedEntity(AnimatedEntity *pEntity)
			{
				this->SetScale(pEntity->GetScale());
				this->SetRotation(pEntity->GetRotation());
				this->SetPosition(pEntity->GetPosition());

				this->vPoses = vector<EntityPose*>();

				this->vRenderModels = vector<Model*>();

				for (int iPoseIndex = 0; iPoseIndex < pEntity->GetPoseCount(); iPoseIndex ++)
				{
					this->AddPose(pEntity->GetPose(iPoseIndex));
				}

				for (int iModelIndex = 0; iModelIndex < pEntity->GetModelCount(); iModelIndex ++)
				{
					this->vModels.push_back(pEntity->GetModel(iModelIndex));
					this->vRenderModels.push_back(pEntity->GetRenderModel(iModelIndex));
				}

				map<string, Animation*> mTempAnimationMap = pEntity->GetAnimationMap();
				for (map<string, Animation*>::iterator itAnimationMap = mTempAnimationMap.begin();
					itAnimationMap != mTempAnimationMap.end(); itAnimationMap ++)
				{
					this->AddAnimation((*itAnimationMap).first, ((Animation*)((*itAnimationMap).second))->GetStartFrame(), 
																((Animation*)((*itAnimationMap).second))->GetEndFrame(), 
																((Animation*)((*itAnimationMap).second))->GetFPS());
				}
			}
			
			AnimatedEntity::~AnimatedEntity()
			{
				this->vPoses.clear();
				this->vRenderModels.clear();
			}

			AnimatedEntity* AnimatedEntity::ShallowCopy(AnimatedEntity *pEntity)
			{
				return new AnimatedEntity(pEntity);
			}

			void AnimatedEntity::DestroyPoses()
			{
				if (!this->vPoses.empty())
				{
					for (unsigned int iPoseIndex = 0; iPoseIndex < this->vPoses.size(); iPoseIndex ++)
						delete this->vPoses[iPoseIndex];

					this->vPoses.clear();
				}
			}

			void AnimatedEntity::DestroyRenderModels()
			{
				if (!this->vRenderModels.empty())
				{
					for (unsigned int iModelIndex = 0; iModelIndex < this->vRenderModels.size(); iModelIndex ++)
					{
						delete this->vRenderModels[iModelIndex];
					}

					this->vRenderModels.clear();
				}
			}

			void AnimatedEntity::AddPose(EntityPose *pPose)
			{
				if (pPose == NULL)
				{
					// You must have a list of models before adding poses
					if (!(this->GetModelCount() == 0))
					{
						EntityPose *pEntityPose = new EntityPose();

						for (int iModelIndex = 0; iModelIndex < this->GetModelCount(); iModelIndex ++)
							pEntityPose->AddModelPose(new ModelPose(this->vModels[iModelIndex]->GetMesh()));

						this->vPoses.push_back(pEntityPose);	
					}
				}
				else
				{
					this->vPoses.push_back(pPose);	
				}
			}
			
			EntityPose *AnimatedEntity::GetPose(int iIndex)
			{
				if ((iIndex < 0) || (iIndex >= (int)this->vPoses.size())) return NULL;

				return this->vPoses[iIndex];
			}

			int AnimatedEntity::GetPoseCount()
			{
				return this->vPoses.size();
			}

			void AnimatedEntity::AddModel(Model* pModel)
			{
				if (pModel == NULL) pModel = Model::CreateBlank();

				Entity::AddModel(pModel);
				this->vRenderModels.push_back(new Model(pModel));
			}

			Model* AnimatedEntity::GetModel(int iIndex)
			{
				return Entity::GetModel(iIndex);
			}

			int AnimatedEntity::GetModelCount()
			{
				return Entity::GetModelCount();
			}

			void AnimatedEntity::UpdateAnimation(float fDelta)
			{
				Animatable::UpdateAnimation(fDelta);
			}

			void AnimatedEntity::UpdateRenderModels(EntityPose* pPose0, EntityPose* pPose1, float fInterpoleValue)
			{
				if (pPose0 == NULL || pPose1 == NULL) return;

				for (int iModelIndex = 0; iModelIndex < this->GetModelCount(); iModelIndex ++)
					this->UpdateRenderModel(iModelIndex, pPose0, pPose1, fInterpoleValue);
			}

			void AnimatedEntity::UpdateRenderModel(int iModelIndex, EntityPose* pPose0, EntityPose* pPose1, float fInterpoleValue)
			{
				ModelPose *pModelPose0 = pPose0->GetModelPose(iModelIndex);
				ModelPose *pModelPose1 = pPose1->GetModelPose(iModelIndex);

				Model *pRenderModel = vRenderModels[iModelIndex];
				Model *pBaseModel = vModels[iModelIndex];
				Mesh *pRenderMesh = pRenderModel->GetMesh();

				pRenderModel->SetTexture(pBaseModel->GetTexture());

				float *pEditVerticePositions = pRenderMesh->GetVertexArray();
				float *pEditVerticeNormals = pRenderMesh->GetNormalArray();

				for (int iVertexIndex = 0; iVertexIndex < pRenderMesh->GetVertexCount(); iVertexIndex ++)
				{
					Vector3 vertexPosition =	Vector3::Lerp( 
												pModelPose0->GetVertexPosition(iVertexIndex), 
												pModelPose1->GetVertexPosition(iVertexIndex), 
												fInterpoleValue );

					Vector3 vertexNormal =		Vector3::Lerp( 
												pModelPose0->GetVertexNormal(iVertexIndex), 
												pModelPose1->GetVertexNormal(iVertexIndex), 
												fInterpoleValue );

					pEditVerticePositions[(iVertexIndex * 3) + 0] = vertexPosition[0];
					pEditVerticePositions[(iVertexIndex * 3) + 1] = vertexPosition[1];
					pEditVerticePositions[(iVertexIndex * 3) + 2] = vertexPosition[2];

					pEditVerticeNormals[(iVertexIndex * 3) + 0] = vertexNormal[0];
					pEditVerticeNormals[(iVertexIndex * 3) + 1] = vertexNormal[1];
					pEditVerticeNormals[(iVertexIndex * 3) + 2] = vertexNormal[2];
				}
			}

			Model* AnimatedEntity::GetRenderModel(int iModelIndex)
			{
				if (iModelIndex < 0 || iModelIndex >= this->GetModelCount()) return NULL;

				if (this->pCurrentAnimation == NULL)
				{
					return this->vModels[iModelIndex];
				}
				else
				{
					Animation *pAnimation = this->GetCurrentAnimation();
					EntityPose *pPose0 = this->GetPose(pAnimation->GetCurrentFrame());
					EntityPose *pPose1 = this->GetPose(pAnimation->GetNextFrame());

					if (pAnimation->GetPlaystyle() == DelGEGraphics_Animation_Playstyle_Loop)
					{
						if (pAnimation->GetCurrentFrame() == pAnimation->GetEndFrame())
							pPose1 = this->GetPose(pAnimation->GetStartFrame());
					}

					this->UpdateRenderModel(iModelIndex, pPose0, pPose1, pAnimation->GetCurrentFrameTimer());
					return this->vRenderModels[iModelIndex];
				}
			}

			void AnimatedEntity::Destroy()
			{
				Entity::Destroy();
				this->DestroyPoses();
				this->DestroyRenderModels();
			}
		}
	}
}